import sys, os, time, zipfile, json, requests, shutil
from urllib.parse import unquote
from time import sleep

# ---- Clear Screen
def UniversalClearScreen():
    if sys.platform.startswith("win32"): os.system("cls")
    elif sys.platform.startswith("cygwin"): return False
    elif sys.platform.startswith("darwin"): os.system("clear")
    elif sys.platform.startswith("liunx"): os.system("clear")
    else: return False
# ---- Clear Screen

# ---- Class
class Downloader:

    @staticmethod
    def unzipData(dataAdderss:str):
        if not os.path.exists("DownloaderBuffers"): os.mkdir("DownloaderBuffers")
        unzipWorkspace = f"DownloaderBuffers/Downloading-{time.time()}"
        with zipfile.ZipFile(dataAdderss) as unzipFile:
            os.mkdir(unzipWorkspace); unzipFile.extractall(path=unzipWorkspace)
        return unzipWorkspace

    @staticmethod
    def processLinks(unzipWorkspace:str):
        manifestAdderss = f"{unzipWorkspace}/manifest.json"
        manifestContents = json.loads(open(manifestAdderss).read())
        results = {"manifestAdderss": manifestAdderss, "manifestContents": manifestContents, "resourceLinks": []}

        for i in range(len(manifestContents["files"])):
            if manifestContents["files"][i]['required']:
                print(f" [Links] Now process Link => {i+1}/{len(manifestContents['files'])}")
                response = requests.get(f"https://addons-ecs.forgesvc.net/api/v2/addon/{manifestContents['files'][i]['projectID']}/file/{manifestContents['files'][i]['fileID']}",
                                        headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0"}, stream=True)
                results["resourceLinks"].append(json.loads(response.content)["downloadUrl"])

        return results

    @staticmethod
    def getDownloadingFileName(url:str, headers):
        filename = ''
        if 'Content-Disposition' in headers and headers['Content-Disposition']:
            disposition_split = headers['Content-Disposition'].split(';')
            if len(disposition_split) > 1:
                if disposition_split[1].strip().lower().startswith('filename='):
                    file_name = disposition_split[1].split('=')
                    if len(file_name) > 1:
                        filename = unquote(file_name[1])
        if not filename and os.path.basename(url):
            filename = os.path.basename(url).split("?")[0]
        if not filename:
            return time.time()
        return filename

    @staticmethod
    def downloadMods(resourceIndex:dict, modpackName:str):
        orgWorkspace = os.getcwd(); downloadingWorkspace = f"DownloaderResult-{modpackName}"
        # if os.path.exists(f"DownloaderResult/{downloadingWorkspace}"): shutil.rmtree(f"DownloaderResult/{downloadingWorkspace}")
        if not os.path.exists("DownloaderResult"): os.mkdir("DownloaderResult")
        if not os.path.exists(f"DownloaderResult/{downloadingWorkspace}"): os.mkdir(f"DownloaderResult/{downloadingWorkspace}")
        if not os.path.exists(f"DownloaderResult/{downloadingWorkspace}/mods"): os.mkdir(f"DownloaderResult/{downloadingWorkspace}/mods")
        for removeCheck in os.listdir():
            if removeCheck.endswith(".downloading"): os.remove(removeCheck)
        for removeCheck in os.listdir(f"DownloaderResult/{downloadingWorkspace}"):
            if os.path.isdir(removeCheck) and removeCheck != "mods": os.remove(removeCheck)
        os.chdir(f"DownloaderResult/{downloadingWorkspace}/mods")
        downloadingCount = 0
        for downloadLink in resourceIndex["resourceLinks"]:
            downloadingCount += 1
            downloadingFileExists = False
            print(f" [Downloading] Now getting data => ({downloadingCount}/{len(resourceIndex['resourceLinks'])})")
            response = requests.get(downloadLink, headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0"}, stream=True)
            downloadingFileSize = response.headers["Content-Length"]
            downloadingFileName = Downloader.getDownloadingFileName(downloadLink, response.headers)
            for continueCheck in os.listdir():
                if continueCheck == downloadingFileName: downloadingFileExists = True
            if downloadingFileExists:
                print(f" ------------- File exsits => ({downloadingCount}/{len(resourceIndex['resourceLinks'])})")
                print(" [Downloading] Found need download file")
                print(" [Downloading] Skip download file step")
            else:
                print(f" ------------- Now downloading => ({downloadingCount}/{len(resourceIndex['resourceLinks'])})")
                print(f" [Downloading] Now downloading: {downloadingFileName}")
                print(f" [Downloading] File size: {downloadingFileSize}"+" Byte(s)")
                print(" [Downloading] Downloading... ")
                with open(downloadingFileName+".downloading", "wb+") as saveingFile:
                    saveingFile.write(response.content); saveingFile.close()
                os.rename(downloadingFileName + ".downloading", downloadingFileName)
                print(" [Downloading] Now Task complete")
        os.chdir(orgWorkspace); return "DownloaderResult/"+downloadingWorkspace

    @staticmethod
    def copyOverrides(unzipWorkspace:str, downloadingResultAdderss:str):
        overridesAddress = f"{unzipWorkspace}/overrides"
        overridesFiles = os.listdir(overridesAddress)
        for copyingFile in overridesFiles:
            try: shutil.copy2(f"{overridesAddress}/{copyingFile}", downloadingResultAdderss)
            except IsADirectoryError: shutil.copytree(f"{overridesAddress}/{copyingFile}", f"{downloadingResultAdderss}/{copyingFile}")

    @staticmethod
    def removeBuffer():
        shutil.rmtree("DownloaderBuffers")
# ---- Class

print(" ---------- Fish ModpackDownloader")
print(" Welcome using Fish-ModpackDownloader")
print(" Version 0.23.1")
print(" If downloader crash, you only redownload!")
print(" Change runing adderss... ", end="")
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
print("Complete")
print(" ---------- Unzip")
userModpackAdderss = input(" [Unzip] Please input your modpack Zip file adderss(Without .zip): ")
print(" [Unzip] Unzipping... ")
downloaderWorkspace = Downloader.unzipData(userModpackAdderss+".zip")
print(" [Unzip] Unzip complete"); UniversalClearScreen()
print(" [Downloader] Start downloading!")
print(" (1/4) ---------- Processing Links")
print(" [Links] Process Links... ")
resourceIndex = Downloader.processLinks(downloaderWorkspace)
print(" [Links] Complete"); sleep(1); UniversalClearScreen()
print(" (2/4) ---------- Links Process Result")
for i in range(len(resourceIndex["resourceLinks"])):
    print(f" [Links] Resource {i+1} => {resourceIndex['resourceLinks'][i]}")
sleep(10); UniversalClearScreen()
print(" (3/4) ----------- Downloading")
downloadingResultAdderss = Downloader.downloadMods(resourceIndex, userModpackAdderss)
print(" [Downloading] Downloading Task all are Completed!")
print(" [Downloading] Now Copy Overrdides... ", end=""); Downloader.copyOverrides(downloaderWorkspace, downloadingResultAdderss); print("Complete")
print(" [Downloading] Downloading Modpack Task complete!"); sleep(3); UniversalClearScreen()
print(" (4/4) ----------- Informations")
versionIndex = resourceIndex["manifestContents"]
print(f" Result folder: {os.getcwd()+'/DownloaderResult'}")
print(f" Minecraft Version: {versionIndex['minecraft']['version']}")
for i in range(len(versionIndex['minecraft']['modLoaders'])): print(f" Mods Loader({i+1}): {versionIndex['minecraft']['modLoaders'][i]['id']}")
print(f" Modpack Name: {versionIndex['name']}")
print(f" Modpack Version: {versionIndex['version']}")
print(f" Modpack Author: {versionIndex['author']}")
input(" [Complete] Press Enter key continue... "); Downloader.removeBuffer()